#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log.hpp"
#include <stdlib.h>


class Sock
{
public:
    //1.创建套接字
    void Socket()
    {
        //AF_INET:IPV4协议  SOCK_STREAM：表示该套接字使用 TCP 流协议
        _sockfd=socket(AF_INET,SOCK_STREAM,0);
        if(_sockfd<0)
        {
            lg(Fatal, "error socket: %s, error code: %d", strerror(errno), errno);
            exit(-1);
        }
    }
    //2.绑定套接字  即创建IPV4地址结构，
    //将本地的IP和端口绑定给套接字    ->网络字节序
    void Bind(uint16_t port)
    {
        //创建IPV4结构
        struct sockaddr_in local;
        local.sin_family=AF_INET;  
        local.sin_port=htons(port);     //网络字节序 
        //特定ip链接  local.sin_addr.s_addr=inet_addr(_ip.c_str());
        local.sin_addr.s_addr=INADDR_ANY;  //接收任意的ip链接
        int ret=bind(_sockfd,(struct sockaddr *)&local,sizeof(local));   
        if(ret<0)
        {
            lg(Fatal, "bind error, %s: %d", strerror(errno), errno);
            exit(-2);
        }
    }
    //3.监听是否有链接请求 经过三次握手后，会将未处理的链接请求放在全连接队列中
    //backlog：设置全连接队列的长度   队列满了后，剩下的连接会放置在半连接队列中
    //该函数将套接字转换成被动模式，只能被动连接
    void Listen()
    {
        if(listen(_sockfd,_backlog)<0)
        {
            lg(Fatal, "listen err: %s, error code: %d", strerror(errno), errno);
            exit(-1);
        }
    }
    //4.Listen只是放入队列，Accpet是从队列中取得连接请求进行链接
    int Accept(string&clientip,uint16_t*clientport)
    {
        //accept根据_listenfd找到指定的套接字监听队列，再创建新的套接字用于通信
        //
        struct sockaddr_in client;
        socklen_t len=sizeof(client);
        int newfd=accept(_sockfd,(struct sockaddr *)&client,&len);
        if(newfd<0)
        {
            lg(Fatal, "error Accept: %s, error code: %d", strerror(errno), errno);
            exit(-3);
        }

        //将获取的客户端的ip和端口传输出去，将网络字节序转换为本地字节
        char ipstr[64];
        //
        inet_ntop(AF_INET,&(client.sin_addr),ipstr,sizeof(ipstr));
        clientip=ipstr;
        *clientport=ntohs(client.sin_port);  //n to h  s
        return newfd;
    }

    //以下客户端使用
    //5.connect连接服务器  传入IP和port   将下面的内容作为报头的内容  ->网络字节序
    bool Connect(string&ip,uint16_t port)
    {
        struct sockaddr_in server;
        server.sin_family=AF_INET;
        server.sin_port=htons(port);

        inet_pton(AF_INET, ip.c_str(), &(server.sin_addr));//作用相同
        //server.sin_addr.s_addr=inet_addr(ip.c_str());   
        socklen_t len=sizeof(server);
        printf("ip号::%s   端口号port::%d _sockfd :: %d\n",ip.c_str(),port,_sockfd);
        int n=connect(_sockfd,(const struct sockaddr*)&server,len);
        if(n<0)
        {
            cerr << "connect error..." << endl; 
            return false;
        }
        return true;
    }
    int get_sockfd()
    {
        return _sockfd;
    }

    void Close()
    {
        close(_sockfd);
    }


    Sock()
    :_listenfd(0)
    ,_backlog(8)
    ,_sockfd(0)
    {}

    ~Sock()
    {}

private:
    int _listenfd;
    int _backlog;  //全连接队列长度
    int _sockfd;
};


